package com.kate.mall.common.utils;

/**
 * @author 新梦想.陈超
 * @version 2021.2
 * @Description: {TODO}
 * @date 2023/3/7 上午10:46
 */

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.kate.mall.common.vo.messsage.RefId;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * map{
 * "id":1
 * "name":"abc"
 * "price": 12.9
 * }
 * map{
 * "id":1
 * "name":"abc"
 * "price": 12.9
 * }
 * VO{
 * private int id;
 * private String name;
 * }
 */
@Component
@Slf4j
public class ListMap2ListVoUtils2 {
    @Resource
    private  RedisUtils redisUtils;

    @Resource
    ObjectMapper objectMapper;
    /**
     * List<Map>转List<vo>
     * @param source
     * @param clazz
     * @param <T>
     * @return
     *知识点1：T 泛型通配符，在编译时才确定T的类型
     *知识点2：? extends T 泛型上限
     *知识点3：反射
     */
    public   <T> List<T> listMap2ListVo(List<Map> source, Class<? extends T> clazz) {
        if (source == null && source.size() <= 0) {
            return null;
        }
        //TODO 1.list结果集合
        List<T> result =new ArrayList<>() ;
        //如果每次source是相同的，那我就将第1次反射的结果缓存起来，当第2次传入相同的source的时候，
        // 就直接从缓存中获取，而不要进行反射，从而提高我们的这个工具的性能，尤其是在高并发场景下
        /*生成源数据的摘要，并以源数据的摘要作为缓存key,value为解析过后的数据*/
        //TODO 2.根据source的md5值，查询缓存
        String md5Key = null;
        try {
            md5Key = MD5Utils.md5Hex(objectToByteArray(source));
            result =redisUtils.get(md5Key,clazz);

            if (result != null) {
                log.info("map2vo 从redis缓存中获取List<Vo>......");
                //TODO https://blog.csdn.net/JokerLJG/article/details/129041452
                //TODO https://blog.csdn.net/weixin_33613947/article/details/125747873
                result = objectMapper.convertValue(result, new TypeReference<List<T>>(){});
                return result;
            }
            result= new ArrayList<>();
            log.info("map2vo 通过反射获取List<Vo>......");
            //TODO 3.获取VO类的属性名
            //a.获取vo类的属性对象
            //Field[] targetFields = clazz.getDeclaredFields(); 无法获取父类的属性   class.getFields() 获取
            Field[] targetFields = ReflectUtil.getFields(clazz);
            //破解封装
            Field.setAccessible(targetFields, true);
            for (Map filedMap : source) {
                T targetObj = map2Vo(filedMap, targetFields, clazz);
                //TODO 7.将对象放入集合
                result.add(targetObj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //TODO 8.将反射解析的结果存到redis
        redisUtils.set(md5Key,result);
        return result;
    }

    /**
     * map转vo
     * @param clazz
     * @param targetFields
     * @param filedMap
     * @param <T>
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @NotNull
    public <T> T map2Vo(Map filedMap,Field[] targetFields, Class<? extends T> clazz) {
        if (filedMap == null) {
            return null;
        }
        if(targetFields==null||targetFields.length==0){
            //a.获取vo类的属性对象
            targetFields = ReflectUtil.getFields(clazz);
            //破解封装
            Field.setAccessible(targetFields, true);
        }
        T targetObj =  null;
        //TODO 4.创建Vo对象
        try {
            targetObj = clazz.newInstance();
            for (Field targetField : targetFields) {
                //b.获取属性名
                String fieldName = targetField.getName();
                //TODO 5.从map中根据属性名为key，来获取值
                Object fieldVale = filedMap.get(fieldName);
                //TODO 6.给vo对象的fieldName属性赋值为fieldVale
                //将原数据类型的double值转为目标数据类的属性类型BigDecimal
                if (targetField.getType().equals(BigDecimal.class)) {
                    targetField.set(targetObj, new BigDecimal(String.valueOf( fieldVale)));
                    continue;
                }
                //TODO 8.2 如果isNew、isHot属性类型integer转boolean
                //isNew、isHot属性类型integer转boolean
                if (targetField.getName().equals("isNew")) {
                    //TODO 8.2.1.将转换的值设置到目标对象上
                    targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
                    continue;
                }

                //isNew、isHot属性类型integer转boolean
                if (targetField.getName().equals("isHot")) {
                    targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
                    continue;
                }
                //TODO 8.3.集合数据的填充(暂时没有写到，待优化)
                if (targetField.getType().equals(List.class)) {
                    continue;
                }
                //TODO 8.4.如果isOnSale属性类型integer转boolean  1是上架  0是未上架
                if (targetField.getName().equals("isOnSale")) {
                    //TODO 8.2.1.将转换的值设置到目标对象上
                    targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
                    continue;
                }
                //TODO 8.4.如果isOnSale属性类型integer转boolean 1是删除 0是未删除
                if (targetField.getName().equals("deleted")||targetField.getName().equals("enabled")) {
                    //TODO 8.2.1.将转换的值设置到目标对象上
                    targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
                    continue;
                }
//                if (targetField.getType().equals(Boolean.class)) {
////                    //TODO 8.2.1.将转换的值设置到目标对象上
////                    targetField.set(targetObj, ((Integer) fieldVale).equals(new Integer(1)));
//                    continue;
//                }
                //TODO 8.4.商品详情中info数据gallery广告图片，json字符串传字符串数组
                //TODO 8.4. 商品详情productList中specifications，json字符转字符串数组
                if (targetField.getName().equals("gallery") || targetField.getName().equals("specifications")) {
                    //TODO 8.2.1.将转换的值设置到目标对象上
                    JSONArray jsonArray = JSONUtil.parseArray(fieldVale);
                    targetField.set(targetObj, (String[]) jsonArray.toArray(new String[]{}));
                    continue;
                }

                //TODO 8.4. 用户信息R(  String )转dto的 LocalDateTime
                if (targetField.getType().equals(LocalDateTime.class)) {
                    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

                    LocalDateTime time = LocalDateTime.parse((String) fieldVale, df);
                    //TODO 8.2.1.将转换的值设置到目标对象上
                    targetField.set(targetObj, time);
                    continue;
                }

                if (targetField.getType().equals(Date.class)) {
//                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                    //TODO 8.2.1.将转换的值设置到目标对象上

                    try {
//                        targetField.set(targetObj, df.parse((String) fieldVale));
                        targetField.set(targetObj, DateUtil.parse((String) fieldVale));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                if(targetField.getName().equals("refId")){
                    RefId refId = map2Vo((Map) fieldVale, null, RefId.class);
                    //TODO 8.2.1.将转换的值设置到目标对象上
                    try {
                        targetField.set(targetObj, refId);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    continue;
                }

                targetField.set(targetObj, fieldVale);

            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return targetObj;
    }

    /**
     * 对象转字节数组
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static byte[] objectToByteArray(Object obj) throws Exception {
        byte[] bytes = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        bytes = baos.toByteArray();
        baos.close();
        return bytes;
    }

}



//    public static void main(String[] args) throws Exception {
//        //1.list<map>
//        Map m1 = new LinkedHashMap();
//        m1.put("id", 1);
//        m1.put("name", "kate");
//        m1.put("floorPrice", 29.9);
//
//        Map m2 = new LinkedHashMap();
//        m2.put("id", 2);
//        m2.put("name", "kate2");
//        m2.put("floorPrice", 59.9);
//
//
//        Map m3 = new LinkedHashMap();
//        m3.put("id", 3);
//        m3.put("name", "kate2");
//        m3.put("floorPrice", 59.9);
//
//        List<Map> source1 = Arrays.asList(m2);
//        List<Map> source2 = Arrays.asList(m3);
//
//        String s1 = MD5Utils.md5Hex(objectToByteArray(source1));
//        String s2 = MD5Utils.md5Hex(objectToByteArray(source2));
//        System.out.println(s1.equals(s2));
//
//        //2.vo---BrandVo
//
//        //3.调用list<map>转list<vo>
////        List<BrandVO> brandVOS = ListMap2VoUtil.map2vo(source, BrandVO.class);
////        System.out.println(brandVOS);
//
//
//        Integer a = 127;
//        Integer b = 127;
//        System.out.println(a == b);//true
//
//        a = 128;
//        b = 128;
//        System.out.println(a == b);//false
//
//
//    }
//}
//
//
//class BrandVO {
//    private int id;
//    private String name;
//
//    public int getId() {
//        return id;
//    }
//
//    public void setId(int id) {
//        this.id = id;
//    }
//
//    public String getName() {
//        return name;
//    }
//
//    public void setName(String name) {
//        this.name = name;
//    }
//
//    @Override
//    public String toString() {
//        return "BrandVO{" +
//                "id=" + id +
//                ", name='" + name + '\'' +
//                '}';
//    }
//}



